package com.iwomy.envpro.platform.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSONObject;
import com.iwomy.envpro.admin.api.entity.SysTenant;
import com.iwomy.envpro.admin.api.feign.RemoteTenantService;
import com.iwomy.envpro.common.core.constant.SecurityConstants;
import com.iwomy.envpro.common.data.tenant.TenantContextHolder;
import com.iwomy.envpro.platform.VO.*;
import com.iwomy.envpro.platform.constant.CpEnum;
import com.iwomy.envpro.platform.constant.TpEnum;
import com.iwomy.envpro.platform.dto.*;
import com.iwomy.envpro.platform.entity.AirMonitorEquipmentEntity;
import com.iwomy.envpro.platform.entity.Aqi;
import com.iwomy.envpro.platform.service.AirMonitorEquipmentService;
import com.iwomy.envpro.platform.service.AnalysisJudgmentService;
import com.iwomy.envpro.platform.utils.AqiUtil;
import com.iwomy.envpro.platform.utils.DataLimitUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.aggregation.Aggregation;
import org.springframework.data.mongodb.core.aggregation.AggregationResults;
import org.springframework.data.mongodb.core.aggregation.ProjectionOperation;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 设备上报数据
 *
 * @author pig
 * @date 2023-08-31 09:28:08
 */
@Service
@Slf4j
public class AnalysisJudgmentServiceImpl implements AnalysisJudgmentService {
	@Resource
	private MongoTemplate mongoTemplate;
	@Resource
	private AirMonitorEquipmentService airMonitorEquipmentService;
	@Resource
	private RemoteTenantService remoteTenantService;

	@Override
	public List<DynamicCloudMapVO> dynamicCloudMap(DynamicCloudMapDTO dynamicCloudMap) throws Exception {
		AirMonitorEquipmentDTO airMonitorEquipmentDTO = new AirMonitorEquipmentDTO();
		airMonitorEquipmentDTO.setTag(dynamicCloudMap.getTag());
		List<AirMonitorEquipmentEntity> airMonitorEquipmentEntities = airMonitorEquipmentService.listByTag(airMonitorEquipmentDTO);
		SysTenant tenant = remoteTenantService.details(TenantContextHolder.getTenantId(), SecurityConstants.FROM_IN).getData();
		if (ObjectUtil.isEmpty(airMonitorEquipmentEntities)) {
			return new ArrayList<>();
		}
		ProjectionOperation po = Aggregation.project()
				.and("monitorEquipmentId").as("monitorEquipmentId")
				.and("recordTime").as("recordTime");
		TpEnum instance = TpEnum.getInstance(dynamicCloudMap.getTp());
		for (CpEnum cpEnum : CpEnum.values()) {
			String varByReportCode = CpEnum.getVarByReportCode(cpEnum.getReportCode());
			if (StrUtil.isEmpty(varByReportCode)) continue;
			po = po.and(instance.equals(TpEnum.REAl_TIME) ? CpEnum.getRtdByReportCode(cpEnum.getReportCode()) : CpEnum.getAvgByReportCode(cpEnum.getReportCode())).as(varByReportCode);
		}
		Aggregation aggregation = Aggregation.newAggregation(
				Aggregation.match(Criteria.where("monitorEquipmentId").in(airMonitorEquipmentEntities.stream().map(AirMonitorEquipmentEntity::getId).collect(Collectors.toList()))),
				Aggregation.match(Criteria.where("recordTime")
						.gte(DataLimitUtil.convert(dynamicCloudMap.getDt0()))
						.lte(DataLimitUtil.convert(dynamicCloudMap.getDt1()))),
				po
		);

		Map<Long, AirMonitorEquipmentEntity> equipmentEntityMap = airMonitorEquipmentEntities.stream()
				.collect(Collectors.toMap(AirMonitorEquipmentEntity::getId, equipmentEntity -> equipmentEntity));

		AggregationResults<JSONObject> results = mongoTemplate.aggregate(aggregation, instance.getName(), JSONObject.class);
		List<JSONObject> mappedResults = results.getMappedResults();
		List<DynamicCloudVO> dynamicCloudList = new ArrayList<>();
		mappedResults.stream().forEach(x -> {
			Aqi aqi = AqiUtil.CountAqi(getAvg(x, CpEnum.PM2_5), getAvg(x, CpEnum.PM10), getAvg(x, CpEnum.CO),
					getAvg(x, CpEnum.NO2), getAvg(x, CpEnum.O3), getAvg(x, CpEnum.SO2));
			int pollutionDegree = AqiUtil.getPollutionDegree(aqi.getAqi());

			DynamicCloudVO dynamicCloudVO = BeanUtil.copyProperties(x, DynamicCloudVO.class);
			BeanUtil.copyProperties(aqi, dynamicCloudVO);
			String degree = AqiUtil.getDegree(pollutionDegree);
			dynamicCloudVO.setPollutionDegree(pollutionDegree);
			dynamicCloudVO.setDegree(degree);
			//抹去分钟后面的数据方便图标展示
			dynamicCloudVO.setRecordTime(x.getLong("recordTime") / instance.getInterval() * instance.getInterval());
			dynamicCloudVO.setRecordData(DateUtil.formatDate(DateUtil.date(dynamicCloudVO.getRecordTime())));
			dynamicCloudVO.setMonitorEquipmentId(x.getLong("monitorEquipmentId"));
			AirMonitorEquipmentEntity airMonitorEquipmentEntity = equipmentEntityMap.get(dynamicCloudVO.getMonitorEquipmentId());
			dynamicCloudVO.setLatitude(airMonitorEquipmentEntity.getLatitude());
			dynamicCloudVO.setLongitude(airMonitorEquipmentEntity.getLongitude());
			dynamicCloudList.add(dynamicCloudVO);
		});
		Map<Long, List<DynamicCloudVO>> groupedData = new HashMap<>();

		// 根据recordTime进行分组
		for (DynamicCloudVO data : dynamicCloudList) {
			Long recordTime = data.getRecordTime();
			if (!groupedData.containsKey(recordTime)) {
				groupedData.put(recordTime, new ArrayList<>());
			}
			groupedData.get(recordTime).add(data);
		}
		Set<Map.Entry<Long, List<DynamicCloudVO>>> entries = groupedData.entrySet();
		List<DynamicCloudMapVO> dynamicCloudMapVOS = new ArrayList<>();
		entries.stream().forEach(entry -> {
			Long key = entry.getKey();
			List<Long> collect = entry.getValue().stream().map(DynamicCloudVO::getMonitorEquipmentId).collect(Collectors.toList());
			List<AirMonitorEquipmentEntity> temp = airMonitorEquipmentEntities.stream().filter(x ->
					!collect.contains(x.getId())
			).collect(Collectors.toList());
			List<DynamicCloudVO> collect1 = temp.stream().map(x -> {
				DynamicCloudVO dynamicCloudVO = new DynamicCloudVO();
				dynamicCloudVO.setMonitorEquipmentId(x.getId());
				AirMonitorEquipmentEntity airMonitorEquipmentEntity = equipmentEntityMap.get(dynamicCloudVO.getMonitorEquipmentId());
				dynamicCloudVO.setLatitude(airMonitorEquipmentEntity.getLatitude());
				dynamicCloudVO.setLongitude(airMonitorEquipmentEntity.getLongitude());
				return dynamicCloudVO;
			}).collect(Collectors.toList());
			List<DynamicCloudVO> dynamicCloudVOS = groupedData.get(key);
			dynamicCloudVOS.addAll(collect1);
			dynamicCloudVOS.sort(Comparator.comparing(DynamicCloudVO::getMonitorEquipmentId));
			DynamicCloudMapVO dynamicCloudMapVO = new DynamicCloudMapVO();
			dynamicCloudMapVO.setList(dynamicCloudVOS);
			dynamicCloudMapVOS.add(dynamicCloudMapVO);
		});

		return dynamicCloudMapVOS;
	}

	@Override
	public List<TrendAnalysisMapVO> trendAnalysis(TrendAnalysisDTO trendAnalysis) throws Exception {
		List<Long> ids = Arrays.stream(trendAnalysis.getMonitorEquipmentIds()).collect(Collectors.toList());

		List<AirMonitorEquipmentEntity> airMonitorEquipmentEntities = airMonitorEquipmentService.listByIds(ids);
		if (ObjectUtil.isEmpty(airMonitorEquipmentEntities)) {
			return new ArrayList<>();
		}
		ProjectionOperation po = Aggregation.project()
				.and("monitorEquipmentId").as("monitorEquipmentId")
				.and("recordTime").as("recordTime");
		TpEnum instance = TpEnum.getInstance(trendAnalysis.getTp());
		for (CpEnum cpEnum : CpEnum.values()) {
			String varByReportCode = CpEnum.getVarByReportCode(cpEnum.getReportCode());
			if (StrUtil.isEmpty(varByReportCode)) continue;
			po = po.and(instance.equals(TpEnum.REAl_TIME) ? CpEnum.getRtdByReportCode(cpEnum.getReportCode()) : CpEnum.getAvgByReportCode(cpEnum.getReportCode())).as(varByReportCode);
		}
//		SysTenant tenant = remoteTenantService.details(TenantContextHolder.getTenantId(),SecurityConstants.FROM_IN).getData();
		Aggregation aggregation = Aggregation.newAggregation(
				Aggregation.match(Criteria.where("monitorEquipmentId").in(trendAnalysis.getMonitorEquipmentIds())),
				Aggregation.match(Criteria.where("recordTime")
						.gte(DataLimitUtil.convert(trendAnalysis.getDt0()))
						.lte(DataLimitUtil.convert(trendAnalysis.getDt1()))),
				po
		);

		Map<Long, AirMonitorEquipmentEntity> equipmentEntityMap = airMonitorEquipmentEntities.stream()
				.collect(Collectors.toMap(AirMonitorEquipmentEntity::getId, equipmentEntity -> equipmentEntity));

		AggregationResults<JSONObject> results = mongoTemplate.aggregate(aggregation, instance.getName(), JSONObject.class);
		List<JSONObject> mappedResults = results.getMappedResults();
		List<TrendAnalysisVO> trendAnalysisVOList = new ArrayList<>();
		mappedResults.stream().forEach(x -> {
			Aqi aqi = AqiUtil.CountAqi(getAvg(x, CpEnum.PM2_5), getAvg(x, CpEnum.PM10), getAvg(x, CpEnum.CO),
					getAvg(x, CpEnum.NO2), getAvg(x, CpEnum.O3), getAvg(x, CpEnum.SO2));
			int pollutionDegree = AqiUtil.getPollutionDegree(aqi.getAqi());

			TrendAnalysisVO trendAnalysisVO = BeanUtil.copyProperties(x, TrendAnalysisVO.class);
			BeanUtil.copyProperties(aqi, trendAnalysisVO);
			String degree = AqiUtil.getDegree(pollutionDegree);
			trendAnalysisVO.setPollutionDegree(pollutionDegree);
			trendAnalysisVO.setDegree(degree);
			//抹去分钟后面的数据方便图标展示
			trendAnalysisVO.setRecordTime(x.getLong("recordTime") / instance.getInterval() * instance.getInterval());
			trendAnalysisVO.setRecordData(DateUtil.formatDate(DateUtil.date(trendAnalysisVO.getRecordTime())));
			trendAnalysisVO.setMonitorEquipmentId(x.getLong("monitorEquipmentId"));
			AirMonitorEquipmentEntity airMonitorEquipmentEntity = equipmentEntityMap.get(trendAnalysisVO.getMonitorEquipmentId());
			trendAnalysisVO.setLatitude(airMonitorEquipmentEntity.getLatitude());
			trendAnalysisVO.setLongitude(airMonitorEquipmentEntity.getLongitude());
			trendAnalysisVOList.add(trendAnalysisVO);
		});
		Map<Long, List<TrendAnalysisVO>> groupedData = new HashMap<>();
		ids.stream().forEach(x -> {
			groupedData.put(x, new ArrayList<>());

		});

		Set<Long> recordTimeSet = trendAnalysisVOList.stream().map(TrendAnalysisVO::getRecordTime).collect(Collectors.toSet());


		//根据设备ID进行分组
		for (TrendAnalysisVO data : trendAnalysisVOList) {
			Long monitorEquipmentId = data.getMonitorEquipmentId();
			groupedData.get(monitorEquipmentId).add(data);
		}
		Set<Map.Entry<Long, List<TrendAnalysisVO>>> entries = groupedData.entrySet();
		List<TrendAnalysisMapVO> trendAnalysisMapVOList = new ArrayList<>();

		//根据设备ID进行分组

		entries.stream().forEach(entry -> {
			Long monitorEquipmentId = entry.getKey();
			List<Long> collect = entry.getValue().stream().map(TrendAnalysisVO::getRecordTime).collect(Collectors.toList());
			List<Long> nonExistentRecordTime = recordTimeSet.stream().filter(x ->
					!collect.contains(x)
			).collect(Collectors.toList());
			List<TrendAnalysisVO> collect1 = nonExistentRecordTime.stream().map(x -> {
				TrendAnalysisVO trendAnalysisVO = new TrendAnalysisVO();
				trendAnalysisVO.setMonitorEquipmentId(monitorEquipmentId);
				AirMonitorEquipmentEntity airMonitorEquipmentEntity = equipmentEntityMap.get(trendAnalysisVO.getMonitorEquipmentId());
				trendAnalysisVO.setLatitude(airMonitorEquipmentEntity.getLatitude());
				trendAnalysisVO.setLongitude(airMonitorEquipmentEntity.getLongitude());
				trendAnalysisVO.setRecordTime(x);
				return trendAnalysisVO;
			}).collect(Collectors.toList());
			List<TrendAnalysisVO> trendAnalysisVOListByMap = groupedData.get(monitorEquipmentId);
			trendAnalysisVOListByMap.addAll(collect1);
			trendAnalysisVOListByMap.sort(Comparator.comparing(TrendAnalysisVO::getRecordTime));
			TrendAnalysisMapVO trendAnalysisMapVO = new TrendAnalysisMapVO();
			trendAnalysisMapVO.setName(equipmentEntityMap.get(monitorEquipmentId).getName());
			trendAnalysisMapVO.setList(trendAnalysisVOListByMap);
			trendAnalysisMapVOList.add(trendAnalysisMapVO);
		});

		return trendAnalysisMapVOList;
	}

	@Override
	public List<Aqi> pollutionAnalysis(PollutionAnalysisDTO pollutionAnalysis) throws Exception {

		ProjectionOperation po = Aggregation.project()
				.and("monitorEquipmentId").as("monitorEquipmentId")
				.and("recordTime").as("recordTime");
		TpEnum instance = TpEnum.getInstance(pollutionAnalysis.getTp());
		for (CpEnum cpEnum : CpEnum.values()) {
			String varByReportCode = CpEnum.getVarByReportCode(cpEnum.getReportCode());
			if (StrUtil.isEmpty(varByReportCode)) continue;
			po = po.and(instance.equals(TpEnum.REAl_TIME) ? CpEnum.getRtdByReportCode(cpEnum.getReportCode()) : CpEnum.getAvgByReportCode(cpEnum.getReportCode())).as(varByReportCode);
		}
//		SysTenant tenant = remoteTenantService.details(TenantContextHolder.getTenantId(),SecurityConstants.FROM_IN).getData();
		Aggregation aggregation = Aggregation.newAggregation(
				Aggregation.match(Criteria.where("monitorEquipmentId").is(pollutionAnalysis.getMonitorEquipmentId())),
				Aggregation.match(Criteria.where("recordTime")
						.gte(DataLimitUtil.convert(pollutionAnalysis.getDt0()))
						.lte(DataLimitUtil.convert(pollutionAnalysis.getDt1().plusDays(1)))),
				po
		);

		AggregationResults<JSONObject> results = mongoTemplate.aggregate(aggregation, TpEnum.DAILY_AVG.getName(), JSONObject.class);
		List<JSONObject> mappedResults = results.getMappedResults();
		List<Aqi> aqiList = new ArrayList<>();
		mappedResults.stream().forEach(x -> {
			Aqi aqi = AqiUtil.CountAqi(getAvg(x, CpEnum.PM2_5), getAvg(x, CpEnum.PM10), getAvg(x, CpEnum.CO),
					getAvg(x, CpEnum.NO2), getAvg(x, CpEnum.O3), getAvg(x, CpEnum.SO2));
			int pollutionDegree = AqiUtil.getPollutionDegree(aqi.getAqi());
			String degree = AqiUtil.getDegree(pollutionDegree);
			aqi.setPollutionDegree(pollutionDegree);
			aqi.setDegree(degree);
			aqiList.add(aqi);
		});
		return aqiList;
	}

	private Double getAvg(JSONObject x, CpEnum cpEnum) {
		Double value = Double.valueOf(x.getOrDefault(CpEnum.getVarByReportCode(cpEnum.getReportCode()), "0.0").toString());
		return value;
	}


	@Override
	public PollutionSourceAnalysisVO pollutionSourceAnalysis(PollutionAnalysisDTO pollutionAnalysis) throws Exception {
		ProjectionOperation po = Aggregation.project()
				.and("monitorEquipmentId").as("monitorEquipmentId")
				.and("recordTime").as("recordTime");
		TpEnum instance = TpEnum.getInstance(pollutionAnalysis.getTp());
		for (CpEnum cpEnum : CpEnum.values()) {
			String varByReportCode = CpEnum.getVarByReportCode(cpEnum.getReportCode());
			if (StrUtil.isEmpty(varByReportCode)) continue;
			po = po.and(instance.equals(TpEnum.REAl_TIME) ? CpEnum.getRtdByReportCode(cpEnum.getReportCode()) : CpEnum.getAvgByReportCode(cpEnum.getReportCode())).as(varByReportCode);
		}
//		SysTenant tenant = remoteTenantService.details(TenantContextHolder.getTenantId(),SecurityConstants.FROM_IN).getData();
		Aggregation aggregation = Aggregation.newAggregation(
				Aggregation.match(Criteria.where("monitorEquipmentId").is(pollutionAnalysis.getMonitorEquipmentId())),
				Aggregation.match(Criteria.where("recordTime")

						.gte(DataLimitUtil.convert(pollutionAnalysis.getDt0()))
						.lte(DataLimitUtil.convert(pollutionAnalysis.getDt1().plusDays(1)))),

				po
		);


		AggregationResults<JSONObject> results = mongoTemplate.aggregate(aggregation, instance.getName(), JSONObject.class);
		List<JSONObject> mappedResults = results.getMappedResults();
		List<Aqi> aqiList = new ArrayList<>();
		mappedResults.stream().forEach(x -> {
			Aqi aqi = AqiUtil.CountAqi(getAvg(x, CpEnum.PM2_5), getAvg(x, CpEnum.PM10), getAvg(x, CpEnum.CO),
					getAvg(x, CpEnum.NO2), getAvg(x, CpEnum.O3), getAvg(x, CpEnum.SO2));
			int pollutionDegree = AqiUtil.getPollutionDegree(aqi.getAqi());
			String degree = AqiUtil.getDegree(pollutionDegree);
			aqi.setPollutionDegree(pollutionDegree);
			aqi.setDegree(degree);
			aqiList.add(aqi);
		});


		// 使用Stream进行分组和统计，并合并成一个List
		aqiList.stream().filter(aqi -> StrUtil.isNotEmpty(aqi.getName()))
				.collect(Collectors.groupingBy(
						Aqi::getName
				));

		PollutionSourceAnalysisVO pollutionSourceAnalysisVO = new PollutionSourceAnalysisVO();

		pollutionSourceAnalysisVO.setPpm25(aqiList.stream().mapToDouble(Aqi::getPmtwIaqi).sum());
		pollutionSourceAnalysisVO.setPpm10(aqiList.stream().mapToDouble(Aqi::getPmteIaqi).sum());
		pollutionSourceAnalysisVO.setPco(aqiList.stream().mapToDouble(Aqi::getCoIaqi).sum());
		pollutionSourceAnalysisVO.setPno2(aqiList.stream().mapToDouble(Aqi::getNo2Iaqi).sum());
		pollutionSourceAnalysisVO.setPo3(aqiList.stream().mapToDouble(Aqi::getO3Iaqi).sum());
		pollutionSourceAnalysisVO.setPso2(aqiList.stream().mapToDouble(Aqi::getSo2Iaqi).sum());


		pollutionSourceAnalysisVO.setPm25(aqiList.stream().filter(x -> CpEnum.PM2_5.getCode().equalsIgnoreCase(x.getName())).count());
		pollutionSourceAnalysisVO.setPm10(aqiList.stream().filter(x -> CpEnum.PM10.getCode().equalsIgnoreCase(x.getName())).count());
		pollutionSourceAnalysisVO.setCo(aqiList.stream().filter(x -> CpEnum.CO.getCode().equalsIgnoreCase(x.getName())).count());
		pollutionSourceAnalysisVO.setNo2(aqiList.stream().filter(x -> CpEnum.NO2.getCode().equalsIgnoreCase(x.getName())).count());
		pollutionSourceAnalysisVO.setO3(aqiList.stream().filter(x -> CpEnum.O3.getCode().equalsIgnoreCase(x.getName())).count());
		pollutionSourceAnalysisVO.setSo2(aqiList.stream().filter(x -> CpEnum.SO2.getCode().equalsIgnoreCase(x.getName())).count());
		AirMonitorEquipmentEntity airMonitorEquipmentEntity = airMonitorEquipmentService.getById(pollutionAnalysis.getMonitorEquipmentId());
		pollutionSourceAnalysisVO.setEquipmentName(airMonitorEquipmentEntity.getName());
		return pollutionSourceAnalysisVO;
	}

	@Override
	public YearOnYearVO yearOnYear(YearOnYearDTO year) throws Exception {
		//不同天的同一小时，或者不同月的同一号
//		（本期数 - 去年同期数）/ 去年同期数 * 100%
		ProjectionOperation po = Aggregation.project()
				.and("monitorEquipmentId").as("monitorEquipmentId")
				.and("recordTime").as("recordTime");
		TpEnum instance = TpEnum.getInstance(year.getTp());
		for (CpEnum cpEnum : CpEnum.values()) {
			String varByReportCode = CpEnum.getVarByReportCode(cpEnum.getReportCode());
			if (StrUtil.isEmpty(varByReportCode)) continue;
			po = po.and(instance.equals(TpEnum.REAl_TIME) ? CpEnum.getRtdByReportCode(cpEnum.getReportCode()) : CpEnum.getAvgByReportCode(cpEnum.getReportCode())).as(varByReportCode);
		}
		LocalDateTime dt0 = year.getDt0().atStartOfDay();
		LocalDateTime dt1 = null;

		TpEnum tempTp = instance == TpEnum.DAILY_AVG ? TpEnum.HOURLY_AVG : TpEnum.DAILY_AVG;
		if (instance == TpEnum.DAILY_AVG) {
			dt1 = dt0.plusDays(1).plusHours(1);
			dt0 = dt0.plusDays(-1);

			instance = TpEnum.HOURLY_AVG;
		} else {

			dt1 = dt0.plusMonths(1).withDayOfMonth(1);
			dt0 = dt0.plusMonths(-1).withDayOfMonth(1);
			instance = TpEnum.DAILY_AVG;
		}
//		SysTenant tenant = remoteTenantService.details(TenantContextHolder.getTenantId(),SecurityConstants.FROM_IN).getData();
		Aggregation aggregation = Aggregation.newAggregation(
				Aggregation.match(Criteria.where("monitorEquipmentId").is(year.getMonitorEquipmentId())),
				Aggregation.match(Criteria.where("recordTime")
						.gte(DataLimitUtil.convert(dt0))
						.lte(DataLimitUtil.convert(dt1))),
				po
		);
		AggregationResults<JSONObject> results = mongoTemplate.aggregate(aggregation, instance.getName(), JSONObject.class);
		List<JSONObject> mappedResults = results.getMappedResults();

		List<AnalysisJudgmentBaseVO> analysisJudgmentBaseVOArrayList = new ArrayList<>();
		//如果是日，按小时分组，月则按日分组
		mappedResults.stream().forEach(x -> {
			Aqi aqi = AqiUtil.CountAqi(getAvg(x, CpEnum.PM2_5), getAvg(x, CpEnum.PM10), getAvg(x, CpEnum.CO),
					getAvg(x, CpEnum.NO2), getAvg(x, CpEnum.O3), getAvg(x, CpEnum.SO2));
			int pollutionDegree = AqiUtil.getPollutionDegree(aqi.getAqi());
			AnalysisJudgmentBaseVO analysisJudgmentBaseVO = BeanUtil.copyProperties(x, AnalysisJudgmentBaseVO.class);
			BeanUtil.copyProperties(aqi, analysisJudgmentBaseVO);
			String degree = AqiUtil.getDegree(pollutionDegree);
			analysisJudgmentBaseVO.setPollutionDegree(pollutionDegree);
			analysisJudgmentBaseVO.setDegree(degree);
			//抹去分钟后面的数据方便图标展示
			analysisJudgmentBaseVO.setRecordTime(x.getLong("recordTime") / tempTp.getInterval() * tempTp.getInterval());
			analysisJudgmentBaseVO.setRecordData(DateUtil.formatDate(DateUtil.date(analysisJudgmentBaseVO.getRecordTime())));
			analysisJudgmentBaseVOArrayList.add(analysisJudgmentBaseVO);
		});


		// 按日分组
		Map<Integer, Map<Integer, AnalysisJudgmentBaseVO>> dayGroup = analysisJudgmentBaseVOArrayList.stream()
				.collect(Collectors.groupingBy(vo -> tempTp == TpEnum.HOURLY_AVG ? DateUtil.date(vo.getRecordTime()).dayOfMonth() : DateUtil.date(vo.getRecordTime()).month() + 1,
						Collectors.toMap(vo -> tempTp == TpEnum.HOURLY_AVG ? DateUtil.date(vo.getRecordTime()).hour(true) : DateUtil.date(vo.getRecordTime()).dayOfMonth(), vo -> vo)));

		int dt0Day = tempTp == TpEnum.HOURLY_AVG ? dt0.getDayOfMonth() : dt0.getMonth().getValue();
		int dt1Day = tempTp == TpEnum.HOURLY_AVG ? dt1.getDayOfMonth() - 1 : dt1.getMonth().getValue() - 1;

		if (ObjectUtil.isNull(dayGroup.get(dt1Day)))
			dayGroup.put(dt1Day, new HashMap<Integer, AnalysisJudgmentBaseVO>());
		if (ObjectUtil.isNull(dayGroup.get(dt0Day)))
			dayGroup.put(dt0Day, new HashMap<Integer, AnalysisJudgmentBaseVO>());


		//循环次数填充以及计算
		int cyclesCount = tempTp == TpEnum.HOURLY_AVG ? 24 : 31;
		List<AnalysisJudgmentBaseVO> current = new LinkedList<>();
		List<AnalysisJudgmentBaseVO> previous = new LinkedList<>();
		List<AnalysisJudgmentBaseVO> growthRate = new LinkedList<>();
		Map<Integer, AnalysisJudgmentBaseVO> dt1Map = dayGroup.get(dt1Day);
		Map<Integer, AnalysisJudgmentBaseVO> dt0Map = dayGroup.get(dt0Day);
		for (int i = 0; i < cyclesCount; i++) {
			AnalysisJudgmentBaseVO analysisJudgmentBaseVO = new AnalysisJudgmentBaseVO();
			AnalysisJudgmentBaseVO currentObj = Optional.ofNullable(dt1Map.get(i)).orElse(new AnalysisJudgmentBaseVO());
			AnalysisJudgmentBaseVO previousObj = Optional.ofNullable(dt0Map.get(i)).orElse(new AnalysisJudgmentBaseVO());
			analysisJudgmentBaseVO.setAqi(calcGrowthRate(currentObj.getAqi(), previousObj.getAqi()));
			analysisJudgmentBaseVO.setO3(calcGrowthRate(currentObj.getO3(), previousObj.getO3()));
			analysisJudgmentBaseVO.setSo2(calcGrowthRate(currentObj.getSo2(), previousObj.getSo2()));
			analysisJudgmentBaseVO.setNo2(calcGrowthRate(currentObj.getNo2(), previousObj.getNo2()));
			analysisJudgmentBaseVO.setPm10(calcGrowthRate(currentObj.getPm10(), previousObj.getPm10()));
			analysisJudgmentBaseVO.setPm25(calcGrowthRate(currentObj.getPm25(), previousObj.getPm25()));
			analysisJudgmentBaseVO.setCo(calcGrowthRate(currentObj.getCo(), previousObj.getCo()));
			current.add(currentObj);
			previous.add(previousObj);
			growthRate.add(analysisJudgmentBaseVO);

		}
		YearOnYearVO yearOnYearVO = new YearOnYearVO();
		yearOnYearVO.setCurrent(current);
		yearOnYearVO.setPrevious(previous);
		yearOnYearVO.setGrowthRate(growthRate);


		return yearOnYearVO;

	}

	/**
	 * 计算增长率
	 *
	 * @param current
	 * @param previous
	 * @return
	 */
	private double calcGrowthRate(double current, double previous) {

		if (previous == 0) return 0;

		//（本期数 - 去年同期数）/ 去年同期数 * 100%
		return (current - previous) / previous * 100;
	}


	@Override
	public SequentialGrowthVO sequentialGrowth(SequentialGrowthDTO sequentialGrowthDTO) throws Exception {
//		（本期数 - 上一期数）/ 上一期数 * 100%。环比能够反映出数据在相邻时间段内的增长或下降情况，可以用于评估业务的季度或月度发展趋势。
//		（本期数 - 上一期数）/ 上一期数 * 100%
		ProjectionOperation po = Aggregation.project()
				.and("monitorEquipmentId").as("monitorEquipmentId")
				.and("recordTime").as("recordTime");
		TpEnum instance = TpEnum.getInstance(sequentialGrowthDTO.getTp());
		for (CpEnum cpEnum : CpEnum.values()) {
			String varByReportCode = CpEnum.getVarByReportCode(cpEnum.getReportCode());
			if (StrUtil.isEmpty(varByReportCode)) continue;
			po = po.and(instance.equals(TpEnum.REAl_TIME) ? CpEnum.getRtdByReportCode(cpEnum.getReportCode()) : CpEnum.getAvgByReportCode(cpEnum.getReportCode())).as(varByReportCode);
		}
		LocalDateTime dt0 = sequentialGrowthDTO.getDt0().atStartOfDay();
		LocalDateTime dt1 = null;

		TpEnum tempTp = instance == TpEnum.DAILY_AVG ? TpEnum.HOURLY_AVG : TpEnum.DAILY_AVG;
		if (instance == TpEnum.DAILY_AVG) {
			dt1 = dt0.plusDays(1);

			instance = TpEnum.HOURLY_AVG;
		} else {

			dt1 = dt0.plusMonths(1).withDayOfMonth(1);
			dt0 = dt0.withDayOfMonth(1);
			instance = TpEnum.DAILY_AVG;
		}
//		SysTenant tenant = remoteTenantService.details(TenantContextHolder.getTenantId(),SecurityConstants.FROM_IN).getData();
		Aggregation aggregation = Aggregation.newAggregation(
				Aggregation.match(Criteria.where("monitorEquipmentId").is(sequentialGrowthDTO.getMonitorEquipmentId())),
				Aggregation.match(Criteria.where("recordTime")

						.gte(DataLimitUtil.convert(dt0))
						.lte(DataLimitUtil.convert(dt1))),

				po
		);
		AggregationResults<JSONObject> results = mongoTemplate.aggregate(aggregation, instance.getName(), JSONObject.class);
		List<JSONObject> mappedResults = results.getMappedResults();

		List<AnalysisJudgmentBaseVO> analysisJudgmentBaseVOArrayList = new ArrayList<>();
		//如果是日，按小时分组，月则按日分组
		mappedResults.stream().forEach(x -> {
			Aqi aqi = AqiUtil.CountAqi(getAvg(x, CpEnum.PM2_5), getAvg(x, CpEnum.PM10), getAvg(x, CpEnum.CO),
					getAvg(x, CpEnum.NO2), getAvg(x, CpEnum.O3), getAvg(x, CpEnum.SO2));
			int pollutionDegree = AqiUtil.getPollutionDegree(aqi.getAqi());
			AnalysisJudgmentBaseVO analysisJudgmentBaseVO = BeanUtil.copyProperties(x, AnalysisJudgmentBaseVO.class);
			BeanUtil.copyProperties(aqi, analysisJudgmentBaseVO);
			String degree = AqiUtil.getDegree(pollutionDegree);
			analysisJudgmentBaseVO.setPollutionDegree(pollutionDegree);
			analysisJudgmentBaseVO.setDegree(degree);
			//抹去分钟后面的数据方便图标展示
			analysisJudgmentBaseVO.setRecordTime(x.getLong("recordTime") / tempTp.getInterval() * tempTp.getInterval());
			analysisJudgmentBaseVO.setRecordData(DateUtil.formatDate(DateUtil.date(analysisJudgmentBaseVO.getRecordTime())));
			analysisJudgmentBaseVOArrayList.add(analysisJudgmentBaseVO);
		});

		Map<Integer, AnalysisJudgmentBaseVO> collect = analysisJudgmentBaseVOArrayList.stream()
				.collect(Collectors
						.toMap(vo -> tempTp == TpEnum.HOURLY_AVG ? DateUtil.date(vo.getRecordTime())
								.hour(true) : DateUtil.date(vo.getRecordTime()).dayOfMonth(), vo -> vo));


		//循环次数填充以及计算
		int cyclesCount = tempTp == TpEnum.HOURLY_AVG ? 24 : 31;
		List<AnalysisJudgmentBaseVO> current = new LinkedList<>();
		List<AnalysisJudgmentBaseVO> growthRate = new LinkedList<>();
		for (int i = 0; i < cyclesCount; i++) {
			AnalysisJudgmentBaseVO currentObj = Optional.ofNullable(collect.get(i)).orElse(new AnalysisJudgmentBaseVO());
			AnalysisJudgmentBaseVO growthRateObj = new AnalysisJudgmentBaseVO();
			AnalysisJudgmentBaseVO firstObj = null;
			if (i == 0 || i == cyclesCount - 1)
				firstObj = new AnalysisJudgmentBaseVO();
			else {
				firstObj = Optional.ofNullable(collect.get(i - 1)).orElse(new AnalysisJudgmentBaseVO());
			}
			growthRateObj.setAqi(calcGrowthRate(currentObj.getAqi(), firstObj.getAqi()));
			growthRateObj.setO3(calcGrowthRate(currentObj.getO3(), firstObj.getO3()));
			growthRateObj.setSo2(calcGrowthRate(currentObj.getSo2(), firstObj.getSo2()));
			growthRateObj.setNo2(calcGrowthRate(currentObj.getNo2(), firstObj.getNo2()));
			growthRateObj.setPm10(calcGrowthRate(currentObj.getPm10(), firstObj.getPm10()));
			growthRateObj.setPm25(calcGrowthRate(currentObj.getPm25(), firstObj.getPm25()));
			growthRateObj.setCo(calcGrowthRate(currentObj.getCo(), firstObj.getCo()));
			current.add(currentObj);
			growthRate.add(growthRateObj);

		}
		SequentialGrowthVO sequentialGrowthVO = new SequentialGrowthVO();
		sequentialGrowthVO.setCurrent(current);
		sequentialGrowthVO.setGrowthRate(growthRate);

		return sequentialGrowthVO;
	}


}
